package xfuzzy.xfghl.codification;

import java.util.List;
import java.util.UUID;

import xfuzzy.lang.Specification;

/**
 * Clase que contiene la codificacion de un cromosoma
 * @author Alberto David Fernandez Fernandez
 * */

public class DoubleChromosome implements Comparable<DoubleChromosome>  {

	/**
	 * Identificador del cromosoma
	 * */
	private String identification;
	
	/**
	 * Codificacion del cromosoma
	 * */
	private String codificacion;
	
	/**
	 * Codificacion de las variables del cromosoma
	 * */
	private int [] variables;
	
	/**
	 * Codificacion de la estructura del cromosoma
	 * */
	private char [] estructura;
	
	/**
	 * Numero de variables del cromosoma
	 * */
	private int numVariables;
	
	/**
	 * Numero de modulos del cromosoma
	 * */
	private int numModulos;
	
	/**
	 * Error cuadratico del cromosoma
	 * */
	private double fitness;
	
	/**
	 * Cromosoma evaluado
	 * */
	private boolean evaluated;
	
	/**
	 * Representacion canonica del cromosoma
	 * */
	private String canonic;
	
	/**
	 * Especificacion del sistema difuso
	 * */
	private Specification specification;
	
	/**
	 * Constructor de la clase
	 * @param numVariables Numero de variables del cromosoma
	 * */
	
	public DoubleChromosome(int numVariables)  {
		
		this.identification = UUID.randomUUID().toString();
		this.numVariables = numVariables;
		this.numModulos = numVariables - 1;
		this.variables = new int[this.numVariables];
		this.estructura = new char[this.numVariables + this.numModulos];
		this.fitness = Double.MAX_VALUE;
		this.codificacion = "";
		this.evaluated = false;
		this.canonic = "";
		this.specification = null;
	}
	
	/**
	 * Constructor de la clase
	 * @param numVariables Numero de variables del cromosoma
	 * @param codificacion Lista de simbolos de la codificacion
	 * */
	
	public DoubleChromosome(int numVariables, List<String> codificacion)  {
		
		this(numVariables);
		int index_estructura = 0, index_variables = 0;
		for (String s : codificacion)  {
			 if (s.compareTo(EncodingSymbols.moduleSymbolString) == 0)
				 this.estructura[index_estructura] = EncodingSymbols.moduleSymbol;
			 else  {
				 this.estructura[index_estructura] = EncodingSymbols.variableSymbol;
				 this.variables[index_variables] = Integer.valueOf(s);
				 index_variables++;
			 }
			 index_estructura++;
		}
	}
	
	/**
	 * Metodo que devuelve la codificacion
	 * @return Codificacion del cromosoma
	 * */
	
	public String getCodificacion() {

		// si se ha generado no se vuelve a generar
		if (this.codificacion.compareTo("") != 0)  {
			return this.codificacion;
		}
		else  {
			int posicion = 0;
			this.codificacion = "";
			// se recorre la estructura del cromosoma
			for (int i = 0; i < this.estructura.length; i++)  {
				 if (this.estructura[i] == EncodingSymbols.moduleSymbol)
					 this.codificacion += EncodingSymbols.moduleSymbol;
				 else  {
					 this.codificacion += this.variables[posicion];
					 posicion++;
				 }
			}
		}
		return codificacion;
	}

	public String getIdentification() {
		return identification;
	}

	public void setIdentification(String identification) {
		this.identification = identification;
	}

	public void setCodificacion(String codificacion) {
		this.codificacion = codificacion;
	}
	
	public int [] getVariables() {
		return variables;
	}

	public void setVariables(int [] variables) {
		this.variables = variables;
	}

	public char [] getEstructura() {
		return estructura;
	}

	public void setEstructura(char [] estructura) {
		this.estructura = estructura;
	}

	public int getNumVariables() {
		return numVariables;
	}

	public void setNumVariables(int numVariables) {
		this.numVariables = numVariables;
	}

	public int getNumModulos() {
		return numModulos;
	}

	public void setNumModulos(int numModulos) {
		this.numModulos = numModulos;
	}
	
	public double getFitness() {
		return fitness;
	}

	public void setFitness(double fitness) {
		this.fitness = fitness;
	}
	
	/**
	 * Representacion del cromosoma
	 * @return String con la representacion del cromosoma
	 * */

	public String toString()  {
		
		String resultado = "";
		for (int index = 0; index < this.variables.length; index++)
			 resultado += String.valueOf(this.variables[index]);
		resultado += " | ";
		for (int index = 0; index < this.estructura.length; index++)
			 resultado += String.valueOf(this.estructura[index]);
		return resultado;
	}
	
	/**
	 * Metodo que realiza una copia del cromosoma
	 * @return Copia del cromosoma
	 * */
	
	public DoubleChromosome copiar()  {
		
		DoubleChromosome copia = new DoubleChromosome(this.numVariables);
		for (int indice = 0; indice < this.variables.length; indice++) 
			 copia.variables[indice] = this.variables[indice];
		
		for (int indice = 0; indice < this.estructura.length; indice++)
			 copia.estructura[indice] = this.estructura[indice];
		
		copia.fitness = this.fitness;
		copia.evaluated = this.evaluated;
		return copia;
	}

	/**
	 * Metodo que compara dos cromosomas
	 * @param o Cromosoma a comparar
	 * @return Valor entero; 0 si son iguales, -1 si es mayor y 1 si es menor
	 * */
	@Override
	public int compareTo(DoubleChromosome o) {
		
		return this.codificacion.compareTo(o.codificacion);
	}
	
	public boolean isEvaluated() {
		return evaluated;
	}

	public void setEvaluated(boolean evaluated) {
		this.evaluated = evaluated;
	}

	public String getCanonic() {
		return canonic;
	}

	public void setCanonic(String canonic) {
		this.canonic = canonic;
	}

	public Specification getSpecification() {
		return specification;
	}

	public void setSpecification(Specification specification) {
		this.specification = specification;
	}
}
